#include "stdafx.h"
#include "GcTool.h"

#include "SmartPtrContext.h"
#include "GcManager.h"
#include "SmartPtrManager.h"

#include <assert.h>

using namespace std;

static bool CompareAddress (LinkNode* left, LinkNode* right)
{
	return 
		(unsigned char*)(void*)(CSmartPtr<void>*)(left) < (unsigned char*)(void*)(CSmartPtr<void>*)(right) ? 
		true : false;
}

void CGcTool::ClassifySmartPtrs( 
	list<LinkNode*>& needGcPtrsList, 
	list<LinkNode*>& userReachablePtrsList, 
	list<LinkNode*>& embeddedPtrsList )
{
	for (list<LinkNode*>::iterator it = needGcPtrsList.begin();
		it != needGcPtrsList.end();
		it++)
	{
		if (CSmartPtrManager::GetInstance()->IsEmbeddedPtr(*it) == true)
		{
			embeddedPtrsList.push_front(*it);
		}
		else
		{
			userReachablePtrsList.push_front(*it);
		}
	}
}

void CGcTool::Mark( 
	list<LinkNode*>& userReachablePtrsList, /*in*/ 
	list<LinkNode*>& embeddedPtrsList, /*in*/
	list<LinkNode*>& usingPtrsList /*out*/)
{
	// clear the last result
	usingPtrsList.clear();

	// mark all point as no use
	// unnecessary to mark userReachable ptrs, which will be mark using right now
	for(list<LinkNode*>::iterator it = embeddedPtrsList.begin();
		it != embeddedPtrsList.end();
		it++)
	{
		CSmartPtrManager::GetInstance()->SetPtrInUse(*it, false);
	}

	//mark all used points
	for (list<LinkNode*>::iterator it = userReachablePtrsList.begin();
		it != userReachablePtrsList.end();
		it++)
	{
		MarkUsingPtrs(*it, embeddedPtrsList, usingPtrsList);
	}
}

// the garbage will only apprear in the embedded ptr list, 
// for we can never think the user reachable ptr as garbage.
UINT CGcTool::DoGcWork( list<LinkNode*>& embeddedPtrList )
{
	UINT collectedObjects = 0;

	// get the useless ptrs from the embeddedPtrList, 
	for (list<LinkNode*>::iterator itEmbedded = embeddedPtrList.begin(); 
		itEmbedded != embeddedPtrList.end(); 
		itEmbedded++)
	{
		if (!CSmartPtrManager::GetInstance()->IsPtrInUse(*itEmbedded))
		{
			// *itEmbedded is not in use, delete it
			DELETE_IF_NOT_NULL(*itEmbedded);
			
			collectedObjects++;
		}
	}
	
	return collectedObjects;
}

void CGcTool::MarkUsingPtrs( 
	LinkNode *beginPtr, 
	list<LinkNode*>& embeddedPtrList, 
	list<LinkNode*>& usingPtrList /*out, just for test, not including NullWrapperObj*/)
{
	// TODO: will it cause deadlock if recursive?
	// Lock l;

	// if NullWrapperObj just skip
	if ((void*)(*(static_cast<CSmartPtr<void>*>(beginPtr))) == NULL)
	{
		// this will only happen when the Null CSmartPtr is user-reachable.
		return;
	}

	// the current ptr must be using
	CSmartPtrManager::GetInstance()->SetPtrInUse(beginPtr, true);

	// add the using ptr into the receive list
	usingPtrList.push_front(beginPtr);

	// later, we need the address scope of ptr
	// only need address, so convert it into CSmartPtr<void>.
	// why void? for the only thing stored in wrapper is T*, and we only compare the address
	unsigned char* ptrBaseAddress = (unsigned char*)(void*)((*static_cast<CSmartPtr<void> *>(beginPtr)));
	size_t scopeSize = CSmartPtrManager::GetInstance()->GetSize(beginPtr);

	for (list<LinkNode*>::iterator it = embeddedPtrList.begin();
		it != embeddedPtrList.end();
		it++)
	{
		CSmartPtr<void>* pointer = static_cast<CSmartPtr<void>* >(*it);
		unsigned char * address = (unsigned char*)(void*)pointer;

		// it "pointer" is in the object that the "beginPtr" is pointing to, 
		// the address of it must be within that object's scope.
		if( !CSmartPtrManager::GetInstance()->IsPtrInUse(pointer) // NullWrapperObj always in-use
			&& (address >= ptrBaseAddress
			&& address < ptrBaseAddress + scopeSize) )
		{
			//point is in scope of ptr
			MarkUsingPtrs(pointer, embeddedPtrList, usingPtrList);
		}
	}
}